namespace RkCalendar.ViewModel
{
    public class CalendarViewModel
    {
        private const int dayCountInWeek = 7;
        private const int weekCountACalendar = 6;
        private DateTime currentDay;
        private Dictionary<int, AlmanacMonthDict> almanacDict;
        public CalendarViewModel(DateTime currentDay)
        {
            this.almanacDict = new Dictionary<int, AlmanacMonthDict>();
            this.currentDay = currentDay;
            this.DayCountInMounth = DateTime.DaysInMonth(this.CurrentYear, this.CurrentMonth);
            this.CurrentDayAlmanac = GetAlmanac(this.CurrentYear, this.CurrentMonth, this.CurrentDay);
        }
        private bool isSundayFirst;
        public bool IsSundayFirst
        {
            get { return isSundayFirst; }
            set
            {
                if (isSundayFirst != value)
                {
                    isSundayFirst = value;
                }
            }
        }

        public int CurrentYear => this.currentDay.Year;
        public int CurrentMonth => this.currentDay.Month;
        public int CurrentDay => this.currentDay.Day;
        public DayOfWeek CurrentDayOfWeek => this.currentDay.DayOfWeek;
        public int DayCountInMounth { get; }
        public Almanac CurrentDayAlmanac { get; }

        private Almanac GetAlmanac(int year, int month, int day)
        {
            if (!this.almanacDict.ContainsKey(year))
            {
                var almanacDatas = TimeHelper.GetAlmanacDatas(year);
                this.almanacDict[year] = new AlmanacMonthDict(year, almanacDatas);
            }
            var yearAlmanacs = this.almanacDict[year];
            if (yearAlmanacs.ContainsKey(month) && yearAlmanacs[month].ContainsKey(day))
                return yearAlmanacs[month][day];
            else
                return null;
        }

        private CalendarDay[] GetCalendarDays()
        {
            var firstDay = new DateTime(this.CurrentYear, this.CurrentMonth, 1);
            var firstDayWeek = firstDay.DayOfWeek;
            int index = this.isSundayFirst ? (int)firstDayWeek : (int)firstDayWeek - 1;
            if (index == -1)
                index = 6;
            firstDay = firstDay - new TimeSpan(index, 0, 0, 0);
            var lastDay = firstDay + new TimeSpan(41,0,0,0);
            CalendarDay[] days = new CalendarDay[weekCountACalendar * dayCountInWeek];
            for (int i = 0; i < 6 * 7; i++)
            {
                if (i < index)
                {
                    int day = firstDay.Day + i;
                    var almanac = GetAlmanac(firstDay.Year, firstDay.Month, day);
                    string lDay = GetAlmanacDayStr(almanac);
                    days[i] = new CalendarDay(firstDay.Year, firstDay.Month, day, lDay);
                }
                else if (i >= index && i < index + this.DayCountInMounth)
                {
                    int day = i - index + 1;
                    var almanac = GetAlmanac(this.CurrentYear, this.CurrentMonth, day);
                    string lDay = GetAlmanacDayStr(almanac);
                    days[i] = new CalendarDay(this.CurrentYear, this.CurrentMonth, day, lDay);
                }
                else
                {
                    int day = i - index - this.DayCountInMounth + 1;
                    var almanac = GetAlmanac(lastDay.Year, lastDay.Month, day);
                    string lDay = GetAlmanacDayStr(almanac);
                    days[i] = new CalendarDay(lastDay.Year, lastDay.Month, day, lDay);
                }
            }
            return days;
        }

        private string GetAlmanacDayStr(Almanac almanac)
        {
            if(almanac.lDate != "初一")
                return almanac.lDate;
            string lmonth = almanac.lMonth;
            if(almanac.lMonth.Length == 1 )
                lmonth = lmonth + "月";
            return lmonth;
        }

        public CalendarText[,] GetCalendarTexts()
        {
            CalendarText[,] calendarTexts = new CalendarText[weekCountACalendar+1, dayCountInWeek];
            CalendarDay[] calendarDays = GetCalendarDays();

            for(int c = 0; c < dayCountInWeek; c++)
            {
                int index = this.isSundayFirst ? c : c+1;
                if(index == 7)
                    index = 0;
                DayOfWeek dayOfWeek = (DayOfWeek)index;
                var foreColor = ConsoleColor.White;
                if(dayOfWeek == DayOfWeek.Sunday || dayOfWeek == DayOfWeek.Saturday)
                    foreColor = ConsoleColor.Red;
                string weekStr = $"\u001B[4m    {TimeHelper.GetDayOfWeekStr(dayOfWeek)}    \u001B[0m";
                CalendarText  text = new CalendarText(weekStr)
                {
                    BackColor = ConsoleColor.DarkGray,
                    ForeColor = foreColor
                };
                calendarTexts[0,c] = text;
            }

            for(int i=0 ; i<calendarDays.Count() ; i++)
            {
                CalendarDay datText = calendarDays[i];
                int row = i / 7 + 1;
                int col = i % 7 ;
                datText.ForeColor = calendarTexts[0,col].ForeColor;
                if(datText.Year == this.CurrentYear
                    && datText.Month == this.CurrentMonth
                    && datText.Day == this.CurrentDay)
                {
                    datText.BackColor = ConsoleColor.DarkBlue;
                    datText.ForeColor = ConsoleColor.Yellow;
                }
                calendarTexts[row, col] = datText;
            }

            return calendarTexts;
        }

        public List<string> GetCurrentDayAlmanacInfo()
        {
            List<string> info = new List<string>();
            if(this.CurrentDayAlmanac == null)
                return info;
            string gzYear = this.CurrentDayAlmanac.gzYear;
            string zodiac = TimeHelper.GetYearOf12ZodiacSigns(gzYear);
            info.Add("🔷 {+Blue}" + $"{this.CurrentYear} {gzYear}年 {zodiac}" + "{/}");
            string lMonth = this.CurrentDayAlmanac.lMonth.EndsWith("月") ? this.CurrentDayAlmanac.lMonth : this.CurrentDayAlmanac.lMonth + "月";
            string status = GetStatus();
            info.Add("✨ {+Yellow}" + $"[{status}]  {this.CurrentMonth}月{this.CurrentDay}日  {lMonth} {this.CurrentDayAlmanac.lDate}" + "{/}");
            string festival = string.IsNullOrEmpty(this.CurrentDayAlmanac.festivalList) ? 
                                "" : "💠 {+Cyan}"+this.CurrentDayAlmanac.festivalList + "{/}";
            info.Add(festival);
            info.Add("✅ {+Green}宜:" + $"{this.CurrentDayAlmanac.suit}" + "{/}");
            info.Add("🚫 {+Red}禁:" + $"{this.CurrentDayAlmanac.avoid}" + "{/}");
            return info;
        }

        private string GetStatus()
        {
            if(this.CurrentDayAlmanac == null)
                return string.Empty;
            if(int.TryParse(this.CurrentDayAlmanac.status, out int status))
            {
                return status >=2 ? "工作日" : "休息日";
            }
            else
            {
                return (this.CurrentDayOfWeek == DayOfWeek.Sunday 
                        || this.CurrentDayOfWeek == DayOfWeek.Saturday) ?
                        "休息日" : "工作日";
            }
        }
    }

    public class AlmanacMonthDict : Dictionary<int,Dictionary<int, Almanac>>
    {
        public int Year { get; }

        public AlmanacMonthDict(int year, List<AlmanacData> almanacDatas)
        {
            this.Year = year;
            foreach (var monthDatas in almanacDatas)
            {
                this[monthDatas.Month] = new Dictionary<int, Almanac>();
                foreach (var dayData in monthDatas.almanac)
                {
                    if (int.TryParse(dayData.day, out int day))
                    {
                        this[monthDatas.Month][day] = dayData;
                    }
                }
            }
            
        }
    }
}

